/*
 *  segmentation
 *
 *  license: GPL2
 *  Copyright (C) i@liufang.org.cn
 *  Author: fang.liu
 */

#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <event2/event.h>
#include "segmentation.h"
#include "segbuf.h"
#include "server.h"

#define PORT 12345
#define BACKLOG 5
#define MEM_SIZE 4096
#define PID_FILE "/tmp/segmentation.pid"

struct event_base *base;

static void save_pid(const char *pid_file) {
    FILE *fp;

    if ((fp = fopen(pid_file, "w")) == NULL) {
        printf("Could not open the pid file %s for writing", pid_file);
        return;
    }

    fprintf(fp,"%ld\n", (long)getpid());

    if (fclose(fp) == -1) {
    	printf("Could not close the pid file %s", pid_file);
    }
}


int main(int argc, char** argv)
{
	int opt;
	char *opt_string = "vhcsw:";
	char *word_file = "/tmp/seg.txt";
	char *tmp;
	int type = 0;
	//有传递参数, 进行参数处理
	if(argc > 1) {
		while ((opt = getopt(argc, argv, opt_string)) != -1) {
			switch (opt) {
				case 'v':
					printf("segmentation version: ");
					printf(SEGMENTATION_VERSION);
					printf("\r\n");
					return 0;
					break;
				case 'w':
					word_file = optarg;
					break;
				case 's': //服务监听模式
					type = 1;
					break;
				case 'c': //标准输入模式
					type = 2;
					break;
				case 'h':
				default:
					printf("segmentation help: \r\n");
					printf("-h segmentation help\r\n");
					printf("-v segmentation version\r\n");
					printf("-w word file path\r\n");
					printf("-n run with server\r\n");
					printf("-s run with std io\r\n");
					return 0;
			}
		}
	}

	init_index(word_file);


	if(type == 1) {
		//退出主进程后台运行程序
		if(fork() > 0) {
			exit(0);
		}
		//开启分词服务监听, 通过套接字方式进行网络请求分词
		seg_server();
	} else if (type == 0) {
		//console
        printf("test: %s\r\n", argv[argc-1]);
		result* r = seg(argv[argc-1]);
		print_result(r);
		free_result(r);
	} else if (type == 2) {
		on_input();
	}

	return 0;

	error: //出错处理

		return 1;
}

/**
 * 接受处理客户端请求
 */
void on_read(int sock, short event, void* arg)
{
    seg_buf* s_buf;
	char buf[MEM_SIZE];
	seg_buf* result_data;
	result* r;
	int n, len;

    s_buf = seg_buf_init();

	if(event == EV_TIMEOUT) {
		//ignore
		return;
	}

	while((n = read(sock, buf, MEM_SIZE))>0) {
        //读取状态判断
        if(n<0) {
            break;
        }

        seg_buf_appendn(s_buf, buf, n);
		if(n!=0 && buf[n-1] != '\n') {
            continue;
        }

		printf("len:%d, data:%s",n, s_buf->data);
		r = seg(s_buf->data);
        print_result(r);
        result_data = string_result(r);
        seg_buf_appendn(result_data, "\n", 1);
		if(r) {
            printf("len: %d; result: %s\r\n", result_data->data_len, result_data->data);
            seg_debugxn(result_data->data, result_data->data_len);
//            n = send(sock, result_data->data, result_data->data_len, MSG_TRUNC);
            n = write(sock, result_data->data, result_data->data_len);
		    printf("send len: %d\r\n", n);
            send(sock, "\r\n", 2, MSG_EOR);
        }

        break;
	}

    //free memory
    free_result(r);
    seg_buf_free(s_buf);

	//无数据返回, 客户端关闭或者  延时严重
	if(n<0) {
		puts("关闭连接");
		close(sock);
	}
}

/**
 * 接受客户端连接
 */
void on_accept(int sock, short event, void* arg)
{
	struct sockaddr_in cli_addr;
	int newfd, sin_size;
	//struct timeval one_seconds = {1,0};
	sin_size = sizeof(struct sockaddr_in);

    //接受监听， 对系统调用错误进行处理
    do{
        newfd = accept(sock, (struct sockaddr*)&cli_addr, &sin_size);
        if (newfd == -1) {
            if (errno == EINTR)
                continue;
            else {
                return;
            }
        }
        break;
    } while (1);


    //on_read(newfd, EV_READ, NULL);

	struct event* read_ev = event_new(base, newfd, EV_TIMEOUT|EV_READ|EV_PERSIST, on_read, NULL);
	//event_base_set(base, read_ev);
	event_add(read_ev, NULL);
}


void seg_server()
{
	struct sockaddr_in my_addr;
	int sock;

	sock = socket(AF_INET, SOCK_STREAM, 0);
	int yes = 1;
	setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
	memset(&my_addr, 0, sizeof(my_addr));
	my_addr.sin_family = AF_INET;
	my_addr.sin_port = htons(PORT);
	my_addr.sin_addr.s_addr = INADDR_ANY;
	bind(sock, (struct sockaddr*)&my_addr, sizeof(struct sockaddr));
	listen(sock, BACKLOG);
	puts("=================================================================\n");
	puts("=============            server runing at port:12345     ========\n");
	puts("=================================================================\n");
	//save pid file
	save_pid(PID_FILE);
	//loop event
    struct event *seg_ev;
    //struct timeval five_seconds = {5,0};
    base = event_base_new();
    seg_ev = event_new(base, sock, EV_TIMEOUT|EV_READ|EV_PERSIST, on_accept, NULL);
    event_add(seg_ev, NULL);
    event_base_dispatch(base);
}

//crate stand io evnet
void on_input()
{
	char buf[MEM_SIZE];
	seg_str* result_str;
	result* r;
	int n;
	while((n = read(STDIN_FILENO, buf, MEM_SIZE))>0) {
		if(n < MEM_SIZE) {
			buf[n] = '\0';
		}
		r = seg(buf);
		//write to client
		result_str = NULL;
		result_str = string_result(r);
		if(result_str != NULL) {
			write(STDOUT_FILENO, result_str->str, result_str->len+1);
			//puts(result_str->str);
		}
		if(n < MEM_SIZE) {
			break;
		}
	}
}